﻿using System;
using System.Linq;
using System.Drawing;
using System.Threading;
using System.Collections.Generic;

using COUP.Core.Entities;

namespace COUP.Core
{
    public static class Calculations
    {
        public static Point[] GetPath(Map TargetMap, Point Start, Point Finsh)
        {
            if (Start == Finsh)
                return null;

            bool Complete = false;
            int CurrentX = 0, CurrentY = 0;

            List<PathingNode> Open = new List<PathingNode>();
            List<PathingNode> Closed = new List<PathingNode>();
            List<PathingNode> Path = new List<PathingNode>();

            PathingNode First = new PathingNode(TargetMap.Tiles[Start.X * TargetMap._width + Start.Y], Start);
            PathingNode Last = new PathingNode(TargetMap.Tiles[Finsh.X * TargetMap._width + Finsh.Y], Finsh);

            if (!Last.Accessibility.Accessible)
                return null;

            Open.Add(First);

            while (Open.Count > 0 && !Complete)
            {
                Open = Open.OrderBy((PathingNode SortNode) => SortNode.F).ToList();

                PathingNode Current = Open[0];
                Open.RemoveAt(0);

                if (Closed.Contains(Current))
                    continue;

                CurrentX = Current.Cordinates.X;
                CurrentY = Current.Cordinates.Y;

                if (Current.Cordinates == Finsh)
                {
                    Closed.Add(Current);
                    Complete = true;
                    break;
                }

                for (int i = 0; i < 8; i++)
                {
                    PathingNode NewNode = new PathingNode(TargetMap.Tiles[(CurrentX + Settings.PathDirection[i, 0]) * TargetMap._width + (CurrentY + Settings.PathDirection[i, 1])],
                        new Point(CurrentX + Settings.PathDirection[i, 0], CurrentY + Settings.PathDirection[i, 1]));

                    if (!NewNode.Accessibility.Accessible || Closed.Contains(NewNode))
                        continue;

                    NewNode.Parent = Current;

                    if (i > 3)
                        NewNode.G = NewNode.Parent.G + 14;
                    else
                        NewNode.G = NewNode.Parent.G + 10;

                    if (Open.Find((PathingNode PathNode) => { return PathNode.Cordinates == NewNode.Cordinates; }) != null)
                    {
                        PathingNode Old = Open.Find((PathingNode PathNode) => { return PathNode.Cordinates == NewNode.Cordinates; });

                        if (Old.G >= NewNode.G)
                        {
                            Old.Parent = Current;
                            if (i > 3)
                                Old.G = Old.Parent.G + 14;
                            else
                                Old.G = Old.Parent.G + 10;
                        }

                        continue;
                    }

                    NewNode.H = Settings.PathEstimate * (Math.Abs(NewNode.Cordinates.X - Finsh.X) + Math.Abs(NewNode.Cordinates.Y - Finsh.Y));

                    Open.Add(NewNode);
                }

                Closed.Add(Current);
            }

            PathingNode Final = Closed.Find((PathingNode PathNode) => { return PathNode.Cordinates == Finsh; });
            bool StartFound = false;

            Path.Add(Final);

            while (!StartFound)
            {
                Path.Add(Path[Path.Count - 1].Parent);

                if (Path[Path.Count - 1].Cordinates == Start)
                    StartFound = true;
            }

            Point[] FinalPath = new Point[Path.Count];

            for (int x = 0; x < Path.Count; x++)
            {
                FinalPath[x] = Path[Path.Count - 1 - x].Cordinates;
            }

            return FinalPath;
        }

        public static int Distance(Point Location, int X, int Y)
        {
            int DistX, DistY;
            if (X < Location.X)
                DistX = Math.Abs(X - Location.X);
            else
                DistX = Math.Abs(Location.X - X);

            if (Y < Location.Y)
                DistY = Math.Abs(Y - Location.Y);
            else
                DistY = Math.Abs(Location.Y - Y);

            return Math.Max(DistX, DistY);
        }

        public static bool CheckUseable(Character Player, ItemTypeEntry Item)
        {
            if (Player.Strength < Item.Str_Req || Player.Agility < Item.Dex_Req ||
                Player.Vitality < Item.Vit_Req || Player.Spirit < Item.Spi_Req)
                return false;

            if (Player.Level < Item.Level)
                return false;

            if (Item.Class == 0)
                return true;

            if (Player.Job < Item.Class || Player.Job.CompareTo((byte)Item.Class) > 5)
                if (!((Player.Job >= 130 && Player.Job <= 145) && Item.Class == 190))
                    return false;

            return true;
        }
    }
}
